package com.homelock.thread;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.homelock.constants.LockCmdEnum;
import com.homelock.processor.BaseMsgProcessor;
import com.homelock.processor.CmdMeta;
import com.pi.base.enumerate.redis.RedisCacheEnum;
import com.pi.base.util.cache.RedisUtil;
import com.pi.base.util.lang.ByteUtil;
import com.pi.smart.client.ins.comm.domain.BaseWifiCmd;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * 消息监听接口
 *
 * @author zoumy chenmf@163.com
 */
@Component
public class ZxLockCmdTask implements ApplicationRunner {
    private static final Logger logger = LoggerFactory.getLogger(ZxLockCmdTask.class);

    private static volatile boolean isRuning = true;
    private static Map<String, BaseMsgProcessor> processorMap;
    static final int CMD_DEV = 0xC1;

    @Value("${task.zx.listen.enable}")
    private boolean listenEnable;

    @Autowired
    List<BaseMsgProcessor> baseMsgProcessors;
    @Autowired
    Executor asyncTaskExecute;

    @Override
    public void run(ApplicationArguments args) {
        if (!listenEnable) {
            logger.info("振兴锁监听线程状态为关闭");
            return;
        }
        asyncTaskExecute.execute(() -> {
            logger.info("[振兴锁监听数据]线程启动");
            if (null == processorMap) {
                logger.error("[振兴锁监听数据]没有可解析数据的handle,线程结束");
                return;
            }

            // 测试使用的key,防止读取正式的数据造成数据丢失
            //   String key = "SMART_DEVICELOCK_WIFI_TEST";

            while (isRuning) {
                BaseWifiCmd zxMsgCmd = null;
                try {
                    zxMsgCmd = RedisUtil.blpop(RedisCacheEnum.SMART_DEVICELOCK_WIFI);
                    //   zxMsgCmd = RedisUtil.blpop(key);

                } catch (ClassNotFoundException e) {
                    logger.error("[振兴锁监听数据]类型转换异常：" + e.getMessage());
                    continue;
                } catch (IOException e) {
                    logger.error("[振兴锁监听数据]redis读取异常：" + e.getMessage());
                    continue;
                    // io异常 需要转换到本地内存队列，同时发送监控信息给管理员
                }
                if (null == zxMsgCmd) {
                    // redis brpop 超时拿到null;
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        logger.error("振兴锁监听线程sleep被中断");
                        shutdown();
                    }
                    continue;

                }
                // 数据解析异常或者blpop超时,重新去读
                int cmdLen = Optional.ofNullable(zxMsgCmd.getCmd())
                        .map(bytes -> bytes.length).orElse(0);
                if (cmdLen < 6) {
                    logger.info("[振兴锁监听数据]指令长度异常- {}", cmdLen);
                    Optional.ofNullable(zxMsgCmd).ifPresent(d -> logger.error("[振兴锁监听数据]指令数据- {}", JSONUtils.toJSONString(d)));
                    continue;
                }
                logger.info("[振兴锁监听数据]指令数据- {}", ByteUtil.byteToHex(zxMsgCmd.getCmd()));
                int cmd = zxMsgCmd.getCmd()[0] & 0xFF;
                logger.info("cmd:{}", ByteUtil.byteToHex(new byte[] {zxMsgCmd.getCmd()[0]}));
                if (cmd < 0 || (cmd & 0xFF) != CMD_DEV) {
                    return;
                }
                int optCode = zxMsgCmd.getCmd()[1] & 0xFF;
                int typeCode = zxMsgCmd.getCmd()[2] & 0xFF;
                LockCmdEnum currentEnum = LockCmdEnum.optCodeOf((byte) optCode);
                String mapKey;
                if (currentEnum.getCode() >= 0) { // <0 只判断一位 后面的第二位不作为这边的消息分类处理
                    mapKey = getKey(optCode, typeCode);
                } else {
                    mapKey = String.valueOf(optCode);
                }
                logger.info("optCode:{}, typeCode:{}, mapKey:{}", optCode, typeCode, mapKey);
                try {
                    if (processorMap.containsKey(mapKey)) {
                        processorMap.get(mapKey).handle(zxMsgCmd);
                    } else {
                        logger.info("[振兴锁监听数据]当前未定义指令- {}", mapKey);
                    }
                } catch (Exception e) {
                    logger.error("", e);
                }
            }
        });
    }


    private String getKey(int mapKey1, int mapKey2) {
        return new StringBuilder().append(mapKey1).append("_").append(mapKey2).toString();
    }


    @PostConstruct
    public void init() {
        processorMap = Maps.newConcurrentMap();
        for (BaseMsgProcessor item : baseMsgProcessors) {
            CmdMeta cmdItem = item.getClass().getAnnotation(CmdMeta.class);
            for (LockCmdEnum value : cmdItem.values()) {
                processorMap.putIfAbsent(value.getValue(), item);
            }
        }
    }

    @PreDestroy
    public void shutdown() {
        isRuning = false;
    }
}
